
package com.esdk.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONConfig;
import com.alibaba.fastjson.JSONObject;
import com.esdk.esdk;
import com.esdk.sql.orm.IRow;
import com.esdk.sql.orm.ParentRow;
import lombok.NonNull;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.json.JsonRender;
import org.nutz.json.JsonTypeHandler;
import org.nutz.json.handler.JsonNumberHandler;
import org.nutz.json.handler.JsonPojoHandler;
import org.nutz.lang.Mirror;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public class EasyBean{
	private static final JSONConfig jc=new JSONConfig();
	static{
		jc.setDateFormat(esdk.time.DATETIME_FORMAT);
		jc.setIgnoreNullValue(true);
//		Json.setDefaultJsonformat(JsonFormat.compact().setIgnoreNull(true));
	}
	private static JsonNumberHandler numberHandler=new JsonNumberHandler(){
		@Override
		public void toJson(Mirror<?> mirror,Object currentObj,JsonRender r,JsonFormat jf) throws IOException{
			jf.setCompact(true);
			if(currentObj!=null&&currentObj instanceof Long longVal &&longVal>10000000000000000L)
				r.writeRaw("\""+currentObj+"\"");
			else
				super.toJson(mirror,currentObj,r,jf);
		}
	};

	private static JsonPojoHandler rowHandler=new JsonPojoHandler(){
		public boolean supportFromJson(Mirror<?> mirror, Object obj) {
			return obj instanceof IRow || obj instanceof EasyBean;
		}

		public boolean supportToJson(Mirror<?> mirror, Object obj, JsonFormat jf) {
			return obj instanceof IRow;
		}
		@Override
		public void toJson(Mirror<?> mirror,Object currentObj,JsonRender r,JsonFormat jf) throws IOException{
			jf.setCompact(true);
			currentObj=((IRow)currentObj).toJsonObject(true);
			r.writeRaw(currentObj.toString());
		}
	};

	/**支持雪花id转换成字符串*/
	private static void handleSnowIdToStr(boolean isConvert){
		if(isConvert){
			Json.addTypeHandler(numberHandler);//4：必须插入在原来的numberHandler之前
			Json.addTypeHandler(rowHandler);
		}else{
			List<JsonTypeHandler> handlers=(List<JsonTypeHandler>)esdk.reflect.getDeclaredFieldValue(new Json(),"handlers");
			handlers.remove(numberHandler);//4：必须插入在原来的numberHandler之前
			handlers.remove(rowHandler);
		}
	}

	/**
	 * @param isConvertSnowIdToStr 是否雪花ID转换成字符串，解决前端js丢失Long类型精度的问题
	 * @param ignoreNull 是否输出值为null的key，默认不输出
	 * */
	public static String toJSONString(@NonNull Serializable bean,boolean ignoreNull,boolean isConvertSnowIdToStr){
		JsonFormat jsonFormat=JsonFormat.compact().setIgnoreNull(ignoreNull);
		BeanToMapConfig beanToMapConfig=new BeanToMapConfig().setIgnoreNull(ignoreNull).setSnowIdToStr(isConvertSnowIdToStr);
		Map map=toMap(bean,beanToMapConfig);
		String result=Json.toJson(map,jsonFormat);
//		String result=JSONUtil.toJsonStr(bean,jc); //解决不了雪花id转换字符问题
		return result.toString();
	}

	public static String toJSONString(Serializable bean,BeanToMapConfig config){
		Map map=toMap(bean,config);
		JsonFormat jsonFormat=JsonFormat.compact().setIgnoreNull(config.isIgnoreNull());
		String result=Json.toJson(map,jsonFormat);
//		String result=JSONUtil.toJsonStr(bean,jc); //解决不了雪花id转换字符问题
		return result.toString();
	}

	public static Map toMap(Serializable bean){
		return toMap(bean,BeanToMapConfig.defaultConfig());
	}

	public static Map toMap(Serializable bean,BeanToMapConfig config){
		if(bean==null)return null;
		Map result=config.isOrdered()?new LinkedHashMap():new HashMap();
		if(bean instanceof Map map){
			for(Iterator iter=map.entrySet().iterator();iter.hasNext();){
				Map.Entry<String,Object> entry=(Map.Entry)iter.next();
				String key=config.isUsePropertyName()?esdk.str.toCamelCase(entry.getKey()):entry.getKey();
				Object value=entry.getValue();
				putValue(result,key,value,config);
			};
		}else{
			Method[] methods=esdk.reflect.findGetterMethods(bean.getClass());
			for(int i=0;i<methods.length;i++){
				Object value=esdk.reflect.invokeMethod(bean,methods[i]);
				String key=esdk.reflect.getFieldName(methods[i],config.isUsePropertyName(),false);
				putValue(result,key,value,config);
			}
		}
		return result;
	}

  private static void putValue(@NonNull Map result,@NonNull String key,Object value,@NonNull BeanToMapConfig config){
		if(value==null){
			if(!config.isIgnoreNull())
				result.put(key,null);
		}
		else if(config.containsKey(value.getClass()))
			result.put(key,config.getHandlers().get(value.getClass()).apply(value));
		else if(value instanceof Collection<?> beanColl){
			Collection mapColl = value instanceof Set?new HashSet():new ArrayList();
			for (Object obj : beanColl) {
				if(obj instanceof ParentRow<?> row){
					mapColl.add(row.toMap(true));
				}else{
					mapColl.add(toMap((Serializable) obj, config));
				}
			}
			result.put(key, mapColl);
		}else if(value.getClass().isArray()){
			if(isPojo(value.getClass().getComponentType())){
				Object[] beanArr = (Object[]) value;
				Map[] mapArr={};
				for(int i = 0; i < beanArr.length; ++i){
					if(beanArr[i] instanceof ParentRow<?> row){
						mapArr[i] = row.toMap(true);
					}else{
						mapArr[i] = toMap((Serializable) beanArr[i], config);
					}
				}
				result.put(key,mapArr);
			}
			else{
				result.put(key,value);
				}
		}
		else if(isPojo(value)||value instanceof Map)
			result.put(key,toMap((Serializable)value,config));
		else
			result.put(key,value);
	}

	public static JSONObject toJSONObject(@NonNull Serializable bean){
		return esdk.json.toJsonObject(toMap(bean));
	}

	public static JSONObject toJSONObject(@NonNull Serializable bean,boolean isIgnoreNull){
		return esdk.json.toJsonObject(toMap(bean,new BeanToMapConfig().setIgnoreNull(isIgnoreNull)));
	}

	/**
	 * @param isConvertSnowIdToStr 是否雪花ID转换成字符串，解决前端js丢失Long类型精度的问题
	 * @param ignoreNull 是否输出值为null的key，默认不输出
	 * */
	public static JSONObject toJSONObject(@NonNull Serializable bean,boolean ignoreNull,boolean isConvertSnowIdToStr){
		BeanToMapConfig beanToMapConfig=new BeanToMapConfig().setIgnoreNull(ignoreNull).setSnowIdToStr(isConvertSnowIdToStr);
		Map map=toMap(bean,beanToMapConfig);
		JSONObject result=esdk.json.toJsonObject(map);
		return result;
	}

	/**
	 * @param obj 不能为null，为null则返回false
	 * */
	public static boolean isPrimitive(@NonNull Object obj){
		Objects.requireNonNull(obj);
		return isPrimitive(obj.getClass());
	}

	public static boolean isPojo(@NonNull Object obj){
		return !isPrimitive(obj)
				&& !obj.getClass().isArray()
				&& !(obj instanceof Iterator<?>)
				&& !obj.getClass().getPackage().getName().startsWith("java")
				&& esdk.reflect.findGetterMethods(obj.getClass()).length>0;
	}
	public static boolean isPrimitive(Class clazz){
		boolean result=clazz.isPrimitive() //
			|| clazz == Boolean.class //
			|| clazz == Character.class //
			|| clazz == Byte.class //
			|| clazz == Short.class //
			|| clazz == Integer.class //
			|| clazz == Long.class //
			|| clazz == Float.class //
			|| clazz == Double.class //
			|| clazz == BigInteger.class //
			|| clazz == BigDecimal.class //
			|| clazz == String.class //
			|| clazz == java.util.Date.class //
			|| clazz.isEnum();
		return result;
	}


	/**
	 * map转换成bean，支持list<XxxBean>
	 * 暂不支持RowSet
	 * */
	public static <T extends Serializable> T mapToBean(Map<String,Object> map,T bean) {
		if(bean==null||map==null)
			return bean;
		try{
			for(Iterator<Map.Entry<String,Object>> item=map.entrySet().iterator();item.hasNext();){
				Map.Entry<String,Object> entry=item.next();
				if(entry.getValue()!=null){
					Method method=esdk.reflect.findSetterMethod(bean.getClass(),entry.getKey());
					if(method!=null){
						if(method.getParameterTypes()[0].equals(entry.getValue().getClass()))
							method.invoke(bean,entry.getValue());
						// 暂不支持内嵌集合
						else if(Collection.class.isAssignableFrom(method.getParameterTypes()[0])){
							Class componentClass=esdk.reflect.getGenericClass(method,0,0);
							// 借用hutool工具类，找到适配的集合类型
							Collection<Object> list=CollUtil.create(method.getParameterTypes()[0], componentClass);
							for(Object o: ((Collection)entry.getValue())){
								if(o instanceof Map<?,?>){
									list.add(mapToBean((Map<String,Object>)o,componentClass));
								}else{
									// 这里Object不会报错，但若元素类型非不匹配则使用过程可能会报错，目前暂时忽略这个问题
									list.add(esdk.obj.valueOf(componentClass,o));
								}
							}
							method.invoke(bean,list);
						}
						// 处理数组类型
						else if (method.getParameterTypes()[0].isArray()) {
							Class<?> componentType = method.getParameterTypes()[0].getComponentType();
							if (entry.getValue() instanceof Collection) {
								Collection<?> collection = (Collection<?>) entry.getValue();
								Object array = Array.newInstance(componentType, collection.size());
								int i = 0;
								for (Object o : collection) {
									if (o instanceof Map) {
										Array.set(array, i++, mapToBean((Map<String, Object>) o, componentType));
									} else {
										Array.set(array, i++, esdk.obj.valueOf(componentType, o));
									}
								}
								method.invoke(bean, array);
							}
						}
						else {
							method.invoke(bean,esdk.obj.valueOf(method.getParameterTypes()[0],entry.getValue()));
						}
					}
				}
			}
			return bean;
		}catch(Exception e){
			throw esdk.tool.wrapThrowble(e);
		}
	}

	public static <T extends Serializable> T mapToBean(Map<String,Object> map,Class<T> clazz) {
		return mapToBean(map,esdk.reflect.safeNewInstance(clazz));
	}

	/**注意：不支持深拷贝*/
	public static <T> T copyBeanProperties(Object from,T to,boolean acceptNull,boolean ignoreCase, Map propertyNameMap,String includesRegex){
		return esdk.reflect.copyBeanProperties(from,to,acceptNull,ignoreCase,propertyNameMap,includesRegex,null);
	}

	/**注意：不支持深拷贝*/
	public static <T> T copyBeanProperties(Object from,T to,boolean acceptNull,boolean ignoreCase, Map propertyNameMap,String includesRegex,String excludesRegex){
		return esdk.reflect.copyBeanProperties(from,to,acceptNull,ignoreCase,propertyNameMap,includesRegex,excludesRegex);
	}

	/**注意：不支持深拷贝*/
	public static <T> T copyBeanProperties(Object from,T to){
		return esdk.reflect.copyBeanProperties(from,to);
	}

	public static <T>T trim(T obj) {
		if(obj==null) return null;
		for(Method m:obj.getClass().getMethods()) {
			if(m.getName().startsWith("get")&&m.getReturnType().equals(String.class)&&m.getParameterTypes().length==0) {
				Method setM=EasyReflect.findSetterMethod(obj.getClass(),m.getName().substring(3));
				if(setM!=null) {
					try{
						Object v=m.invoke(obj);
						Object v1=esdk.obj.trim(v);
						if(v!=null&&!v1.equals(v))
							setM.invoke(obj,v1);
					}catch(Exception e){
						esdk.tool.wrapThrowble(e);
					}
				}
			}
		}
		return obj;
	}
}

